package soul.rpc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Lifecycle;
import org.springframework.stereotype.Component;
import server.Srpc;
import server.SrpcRequest;
import server.SrpcResponse;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by sunzhengjie on 2017/1/5.
 */
//@Component
public class RpcServer implements InitializingBean, Lifecycle, ApplicationContextAware {

    /**
     * 服务端口号
     **/
    private int port = 12000;

    private ServerSocket server;

    private Executor executorService = Executors.newCachedThreadPool();

    private Map<String, Object> handlerMap = new ConcurrentHashMap<>();

    public void publishedService() throws Exception {

        server = new ServerSocket(port);

        // 一直服务
        for (; ; ) {
            try {
                // 获取socket
                final Socket socket = server.accept();
                executorService.execute(() -> {
                    try {
                        // 获取input
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                        ObjectOutputStream output = new ObjectOutputStream(socket
                                .getOutputStream());
                        try {
                            SrpcRequest request =  (SrpcRequest)input.readObject();
                            try {
                                SrpcResponse response = doHandle(request);
                                output.writeObject(response);
                            } finally {
                                input.close();
                            }
                        } finally {
                            socket.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    private SrpcResponse doHandle(SrpcRequest request) {
        SrpcResponse response = new SrpcResponse();
        response.setRequestId(request.getRequestId());
        try {
            Object service = handlerMap.get(request.getInterfaceName());
            Method method = service.getClass().getMethod(request.getMethodName(),
                    request.getParameterTypes());
            response.setResult(method.invoke(service, request.getParameters()));

        } catch (Exception e) {
            response.setError(e);
        }
        return response;
    }

    public void init() {

    }

    /**
     * @see InitializingBean#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //发布服务
        publishedService();
    }

    /**
     * @see Lifecycle#start()
     */
    @Override
    public void start() {
    }

    /**
     * @see Lifecycle#stop()
     */
    @Override
    public void stop() {
        if (server != null) {
            try {
                server.close();
            } catch (IOException e) {

            }
        }
    }

    /**
     * @see Lifecycle#isRunning()
     */
    @Override
    public boolean isRunning() {
        return false;
    }

    /**
     * @see ApplicationContextAware#setApplicationContext(ApplicationContext)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(Srpc.class);
        System.out.println(serviceBeanMap);
        if (serviceBeanMap != null && !serviceBeanMap.isEmpty()) {
            for (Object serviceBean : serviceBeanMap.values()) {
                String interfaceName = serviceBean.getClass().getAnnotation(Srpc.class).interf().getName();
                handlerMap.put(interfaceName, serviceBean);
            }
        }
    }

    /**
     * Setter method for property <tt>executorService</tt>.
     *
     * @param executorService value to be assigned to property executorService
     */
    public void setExecutorService(Executor executorService) {
        this.executorService = executorService;
    }

}
